using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework;
using Masood.MobileGames.SlidingLib.Box;
using Masood.MobileGames.SlidingLib.Enum;

namespace Masood.MobileGames.SlidingLib.Abstract
{
    public abstract class AbstractGameBoard
    {
        #region constructors
        public AbstractGameBoard(int noOfRows, int noOfCols, int width, int height)
        {
            this.x = 0;
            this.y = 0;
            this.width = width;
            this.height = height;
            
            this.boxWidth = 100;
            this.boxHeight = 100;
            
            this.noOfRows = noOfRows;
            this.noOfCols = noOfCols;

            this.boxes = new List<AbstractBox>();

            CalculateBoxWidthAndHeight();
        }
        #endregion

        private void CalculateBoxWidthAndHeight()
        {
            if (noOfCols > 0)
                this.boxWidth = (int)(this.width / this.noOfCols);

            if(noOfRows > 0)
                this.boxHeight = (int)(this.height / this.noOfRows);
        }

        

        #region private variables

        protected List<Texture2D> imageTextures = new List<Texture2D>();

        protected int x;
        protected int y;
        protected int width;
        protected int height;
        protected int boxWidth;
        protected int boxHeight;

        protected List<AbstractBox> boxes;
        protected AbstractBox emptyBox;

        protected int noOfCols;
        protected int noOfRows;
        #endregion

        #region properties
        private Texture2D sourceImageTexture;
        public Texture2D SourceImageTexture
        {
            get { return sourceImageTexture; }
            set { sourceImageTexture = value; }
        }

        protected Rectangle clientBounds;
        public Rectangle ClientBounds
        {
            get { return clientBounds; }
            set { clientBounds = value; }
        }

        public List<Texture2D> ImageTextures
        {
            set { imageTextures = value; }
            get { return imageTextures; }
        }

        public int Cols
        {
            get { return noOfCols; }
            set { noOfCols = value; }
        }

        public int Rows
        {
            get { return noOfRows; }
            set { noOfRows = value; }
        }

        protected MusicState musicState;
        public MusicState MusicState
        {
            get { return musicState; }
            set { musicState = value; }
        }

        protected AbstractBox resetButton;
        public AbstractBox ResetButton
        {
            get { return resetButton; }
            set { resetButton = value; }
        }

        protected AbstractBox playButton;
        public AbstractBox PlayButton
        {
            get { return playButton; }
            set { playButton = value; }
        }

        protected AbstractBox musicButton;
        public AbstractBox MusicButton
        {
            get { return musicButton; }
            set { musicButton = value; }
        }

        public int BoxWidth
        {
            set 
            { 
                boxWidth = value;
                CalculateBoxWidthAndHeight();
            }
            get
            {
                return boxWidth;
            }
        }

        public int BoxHeight
        {
            set 
            { 
                boxHeight = value;
                CalculateBoxWidthAndHeight();
            }
            get
            {
                return boxHeight;
            }
        }

        public int Left
        {
            get { return x; }
        }

        public int Top
        {
            get { return y; }
        }

        public int Right
        {
            get { return x + width; }
        }

        public int Bottom
        {
            get { return y + height; }
        }

        protected Microsoft.Xna.Framework.Content.ContentManager content;
        public ContentManager Content
        {
            set { content = value; }
            get { return content; }
        }
        #endregion

        #region actions
        private void Create()
        {
            int counter = 0;
            //int noOfColumns = (this.width / this.boxWidth);
            //int noOfRows = (this.height / this.boxHeight);
            int totalBoxes = this.noOfCols * noOfRows;
            AbstractBox tempBox = null;

            boxes = new List<AbstractBox>();

            for (int x = 0; x <= this.width - this.boxWidth; x = x + this.boxWidth)
            //for (int y = 0; y <= this.height - this.boxHeight; y = y + this.boxHeight)
            {
                for (int y = 0; y <= this.height - this.boxHeight; y = y + this.boxHeight)
                //for (int x = 0; x <= this.width - this.boxWidth; x = x + this.boxWidth)
                {
                    ++counter;
                    if (counter < totalBoxes)
                    {
                        tempBox = CreateBox(x, y, counter);
                        tempBox.Content = this.Content;
                        tempBox.GameBoard = this;
                        if (this.imageTextures.Count > 0)
                            tempBox.Texture = this.imageTextures[counter - 1];

                        boxes.Add(tempBox);
                    }
                    else
                    {
                        emptyBox = new NumberBox(Content, x + this.Left, y + this.Top, this.boxWidth, this.boxHeight);
                        tempBox.GameBoard = this;
                    }
                }
            }
        }

        public abstract AbstractBox CreateBox(int x, int y, int counter);

        public void Draw(SpriteBatch spriteBatch)
        {
            foreach (AbstractBox bx in boxes)
                bx.Draw(spriteBatch);

            //if (resetButton != null)
            //    resetButton.Draw(spriteBatch);

            //if (playButton != null)
            //    playButton.Draw(spriteBatch);

            //if (musicButton != null)
            //    musicButton.Draw(spriteBatch);
        }

        private bool IsInRange(int clickedX, int clickedY)
        {
            if ((clickedX >= this.Left && clickedX <= this.Right) &&
                (clickedY >= this.Top && clickedY <= this.Bottom))
                return true;

            return false;
        }

        public void PerformSwap(int clickedX, int clickedY)
        {
            if (IsInRange(clickedX, clickedY))
            {
                AbstractBox boxToSwap = FindBox(clickedX, clickedY);
                if (boxToSwap != null)
                {
                    boxToSwap.Swap(emptyBox);
                }
            }
        }

        private AbstractBox FindBox(int clickedX, int clickedY)
        {
            AbstractBox boxToSwap = null;
            foreach (AbstractBox box in boxes)
            {
                if (box.IsInRange(clickedX, clickedY))
                {
                    boxToSwap = box;
                    break;
                }
            }

            return boxToSwap;
        }

        public void Shuffle()
        {
            for (int i = 0; i < 1000; i++)
            {
                AbstractBox boxToSwap = GetRandomButtonNearEmptyBox();
                if (boxToSwap != null)
                    boxToSwap.Swap(emptyBox);
            }
        }

        private AbstractBox GetRandomButtonNearEmptyBox()
        {
            IList<AbstractBox> boxesOnSameRow = GetSwapableBoxes();
            Random random = new Random();

            return boxesOnSameRow[random.Next(boxesOnSameRow.Count)];
        }

        private IList<AbstractBox> GetSwapableBoxes()
        {
            IList<AbstractBox> tempBoxes = new List<AbstractBox>();

            foreach (AbstractBox box in boxes)
            {
                if (box.CanSwap(emptyBox))
                    tempBoxes.Add(box);
            }

            return tempBoxes;
        }

        public void NewGame()
        {
            Clear();
            Create();
            Shuffle();
        }

        public void Reset()
        {
            Clear();
            Create();
        }

        private void Clear()
        {
            boxes.Clear();
        }

        public void PerformSwap(Microsoft.Xna.Framework.Input.Touch.TouchLocation firstTouchLocation)
        {
            if (firstTouchLocation.State == Microsoft.Xna.Framework.Input.Touch.TouchLocationState.Pressed)
                PerformSwap((int)firstTouchLocation.Position.X, (int)firstTouchLocation.Position.Y);
        }

        #endregion

        
    }
}
